// 封装 axios
import axios from 'axios'
import store from '@/store/store'
import { Toast } from 'antd-mobile'
import { customHistory } from './history'
import { logout, updateToken } from '@/store/module/login'
import { setToken } from './token'

const baseURL = 'http://toutiao.itheima.net/v1_0'
//   process.env.NODE_ENV === 'development' ? '/api' : process.env.REACT_APP_URL

const http = axios.create({
    // 接口的基础路径
    // baseURL: 'http://toutiao.itheima.net/v1_0',
    // baseURL: 'http://geek.itheima.net/v1_0',
    // withCredentials: true,
    baseURL,
    timeout: 5000,
})

// 使用 拦截器 后，请求的执行过程：
//
// 1 http.get() => 2 请求拦截器
//    ===> 浏览器将请求发送，服务器接收到请求 ===> 服务器处理并响应请求
//    => 3 响应拦截器 => 4 拿到本次请求的结果（const res = await http.get()）

// 请求拦截器
http.interceptors.request.use(config => {
    const {
        login: { token },
    } = store.getState()

    // 除了登录请求外，其他请求统一添加 token
    if (!config.url?.startsWith('/authorizations') && config.headers) {
        config.headers.Authorization = `Bearer ${token}`
    }

    // http://xxx.com/authorizations?redirect=/home
    // if (!config.url?.startsWith('/authorizations')) {
    //   // 此处，需要使用 非空断言（TS类型） 来去掉 headers 类型中的 undefined 类型
    //   config.headers!.Authorization = `Bearer ${token}`

    //   // ! 是 tS 中的类型，叫做：非空断言，用来从类型中去掉 null 或 undefined
    //   // const headers = config.headers!
    //   // headers.Authorization = `Bearer ${token}`

    //   // 如果使用了 ?. 那么，这个 表达式 不能出现在 等号 左边
    //   // const a = config.headers?.Authorization
    //   // config.headers?.Authorization = `Bearer ${token}`
    // }

    return config
})

// 响应拦截器
http.interceptors.response.use(undefined, async error => {
    // 响应失败时，会执行此处的回调函数

    if (!error.response) {
        // 网路超时
        Toast.show({
            content: '网络繁忙，请稍后再试',
            duration: 1000,
        })
        return Promise.reject(error)
    }

    if (error.response.status === 401) {
        try {
            // token 失效，需要使用 refresh_token 获取新的 token
            // console.log('token 过期了， 重新登录')

            // 从 redux 中获取 refresh_token
            const {
                login: { refresh_token },
            } = store.getState()
            if (!refresh_token) {
                // 如果 refresh_token 不存在，直接重定向到登录页面即可
                // 1 直接拷贝 try..catch 中 catch 的代码逻辑
                //   封装成一个函数，哪个地方用到了，哪个地方调用
                // Toast.show({
                //   content: '登录超时，请重新登录',
                //   duration: 1000,
                //   afterClose: () => {
                //     // 清楚 token
                //     store.dispatch(logout())

                //     // push 方法的第二个参数，就是在路由跳转时携带的额外数据
                //     customHistory.push('/login', {
                //       from: customHistory.location.pathname,
                //     })
                //   },
                // })
                // return Promise.reject(error)

                // 2 手动抛出异常，让 try 代码块捕获错误，然后，就会执行 catch 中的代码
                throw new Error(error)

                // 3 Promise.reject() 用来创建一个失败的Promise对象，对应的 Promise 状态为： rejected
                //   注意：此处一定要添加 await，否则，try..catch 无法捕获 Promise 的错误
                //   对于 Promise 来说，只要 Promise 失败了，就会被 try..catch 捕获，也就会执行 catch 中的代码了
                // await Promise.reject(error)
            }

            // 如果 refresh_token 存在，就换取新的 token
            // 1 不能使用 http.put 而应该使用 axios.put
            //  因为如果使用 http.put 就又会走到 http 设置的请求拦截器
            //  请求拦截器中，就会为其添加 token ，而这个 token 已经是过期的 token，添加进来也没用
            //  所以，此处使用 http 没有效果。因此，此处，我们直接使用原生 axios 来发送请求，并且手动传入 refresh_token 作为请求头
            // 2 注意：代码执行到此处，说明 token 失效了，所以，此处传入的请求头中，值应该是： refresh_token
            // 3 注意：由于使用原生的 axios 所以，需要自己指定完整的接口地址
            const res = await axios.put(
                'http://geek.itheima.net/v1_0/authorizations',
                null,
                {
                    headers: {
                        Authorization: `Bearer ${refresh_token}`,
                    },
                }
            )
            // console.log('换取token', res)
            const newToken = {
                refresh_token,
                token: res.data.data.token,
            }
            // 更新本地存储中的token值
            setToken(newToken)
            // 更新redux中的token值
            store.dispatch(updateToken(newToken))

            // console.log('继续完成原来要执行的操作', error)
            // 可以通过 error.config 来拿到原来要执行的操作
            // 所以，此处，直接通过 http(config) 这种方式，来继续完成原来要执行的操作即可
            return http(error.config)
        } catch (e) {
            // 如果代码出现了异常，就重定向到登录页面
            Toast.show({
                content: '登录超时，请重新登录',
                duration: 1000,
                afterClose: () => {
                    // 清除 token
                    store.dispatch(logout())

                    // push 方法的第二个参数，就是在路由跳转时携带的额外数据
                    customHistory.push('/login', {
                        from: customHistory.location.pathname,
                    })
                },
            })
        }
    }

    return Promise.reject(error)
})

export { http }
